﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Script.Serialization;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;

namespace EasyClass {
	public class Controller {
		public static Controller GetController;
		private MainForm mainForm;
		private DataClass dataClass;
		private MyNetwork myNetwork;
		//private IssueTaskForm issueTaskForm;
		private JavaScriptSerializer jss = new JavaScriptSerializer();

		public void SetMainForm(MainForm m) {
			mainForm = m;
		}

		public void SetDataClass(DataClass d) {
			dataClass = d;
		}

		public void SetMyNetwork(MyNetwork n) {
			myNetwork = n;
		}

		/*public void SetIssueTaskForm(IssueTaskForm i) {
			issueTaskForm = i;
		}

		public void IssueTask(string taskName) {
			Dictionary<string, string> issue = new Dictionary<string, string>();
			Task task = dataClass.GetTask(taskName);
			issue.Add("MsgType", "IssueTask");
			issue["TaskName"] = taskName;
			issue["NProblems"] = task.problems.Count.ToString();
			int cnt = 0;
			foreach (Problem p in task.problems) {
				issue["Problem" + cnt.ToString()] = p.ToJSON();
				cnt++;
			}
			myNetwork.SendClient(IPAddress.Broadcast, jss.Serialize(issue));
		}*/

		/* About Tasks */
		public string[] GetAllTasksName() {
			return dataClass.GetAllTasksName();
		}
		
		public string GetSelectedTaskName() {
			return mainForm.GetSelectedTaskName();
		}

		public Task GetTask(string taskName) {
			return dataClass.GetTask(taskName);
		}

		public bool AddTask(string taskName) {
			if (Array.IndexOf(GetAllTasksName(), taskName) >= 0) return false;
			mainForm.AddTask(taskName);
			dataClass.AddTask(new Task(taskName));
			return true;
		}

		public bool DeleteTask(string taskName) {
			dataClass.DeleteTask(taskName);
			return true;
		}

		public void AddProblem(string description, string[] choices, string answers, string taskName, bool single) {
			MyConsole.WriteLine("AddProblem " + taskName + " " + description);
			Task task = dataClass.GetTask(taskName);
			Problem problem = new Problem(task, description, choices, answers, Convert.ToInt32(single));
			dataClass.AddProblem(taskName, problem);
			mainForm.UpdateTabPage(dataClass.GetTask(taskName));
		}

		public void UpdateTaskPanel(Task task) {
			mainForm.UpdateTabPage(task);
		}


		/* About Topics */
		public string[] GetAllTopicsName() {
			return dataClass.GetAllTopicsName();
		}

		public string GetSelectedTopicName() {
			return mainForm.GetSelectedTopicName();
		}

		public Topic GetTopic(string topicName) {
			return dataClass.GetTopic(topicName);
		}

		public bool AddTopic(string topicName, string description, int low, int upper, int maxTeams) {
			if (Array.IndexOf(GetAllTopicsName(), topicName) >= 0) {
				MessageBox.Show("Topic name exists");
				return false;
			}
			mainForm.AddTopic(topicName);
			Topic topic = new Topic(topicName, description, low, upper, maxTeams);
			dataClass.AddTopic(topic);

			//debug
			//string reason = "";
			//AddTeam(topicName, "bantudui", "tu", "tu", ref reason).ToString();
			//AddTeam(topicName, "bantuduishenniu", "tub", "tu", ref reason).ToString();
			
			return true;
		}

		public void DeleteTopic(string topicName) {
			dataClass.DeleteTopic(topicName);
		}

		public Team AddTeam(string topicName, string teamName, string captainID, string password, ref string reason) {
			Topic topic = GetTopic(topicName);
			if (topic == null) {
				reason = "Topic does not exist";
				return null;
			}
			if (topic.teams.Count >= topic.maxTeams) {
				reason = "Topic is full";
				return null;
			}
			Team team = new Team(GetTopic(topicName), teamName, captainID, password);
			if (dataClass.AddTeam(team, ref reason)) {
				//mainForm.UpdateTabPage(dataClass.GetTopic(topicName));
				return team;
			}
			return null;
		}

		public void UpdateTopicPanel(Topic topic) {
			mainForm.UpdateTabPage(topic);
		}


		public void HandleReceivedData(IPAddress ipAddress, string content) {
			//JavaScriptSerializer jss = new JavaScriptSerializer();
			Dictionary<string, string> dict = jss.Deserialize<Dictionary<string, string>>(content);
			Dictionary<string, string> back = new Dictionary<string, string>();
			string reason = "";
			int idx;
			switch (dict["MsgType"]) {
				case "Register" :
						back["MsgType"] = "Register";
						if (dataClass.TryRegister(dict, ref reason)) {
							back["IfSucceed"] = "Yes";
						} else {
							back["IfSucceed"] = "No";
							back["Reason"] = reason;
						}
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;
				case "Login":
						back["MsgType"] = "Login";
						if (dataClass.TryLogin(dict, ref reason)) {
							back["IfSucceed"] = "Yes";
						} else {
							back["IfSucceed"] = "No";
							back["Reason"] = reason;
						}
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "Logoff":
						back["MsgType"] = "Logoff";
						if (dataClass.TryLogoff(dict, ref reason)) {
							back["IfSucceed"] = "Yes";
						} else {
							back["IfSucceed"] = "No";
							back["Reason"] = reason;
						}
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "MainPage":
						back["MsgType"] = "MainPage";

						/* task */
						string[] taskList = GetAllTasksName();
						back["NTasks"] = taskList.Length.ToString();
						idx = 0;
						foreach (string taskName in taskList) {
							back["TaskName" + idx.ToString()] = taskName;
							back["TaskStatus" + idx++.ToString()] = GetTask(taskName).status.ToString();
						}

						/* topic */
						string[] topicList = GetAllTopicsName();
						back["NTopics"] = topicList.Length.ToString();
						idx = 0;
						foreach (string topicName in topicList) {
							back["TopicName" + idx.ToString()] = topicName;
							back["TopicStatus" + idx++.ToString()] = GetTopic(topicName).status.ToString();
						}

						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "GetTask":
						back = GetTask(dict["TaskName"]).ToDict();
						back["MsgType"] = "GetTask";
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "SubmitAnswer":
						back["MsgType"] = "SubmitAnswer";
						if (dataClass.TrySubmitAnswer(dict, ref reason)) {
							back["IfSucceed"] = "Yes";
						} else {
							back["IfSucceed"] = "No";
							back["Reason"] = reason;
						}
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "GetTopic":
						back = GetTopic(dict["TopicName"]).ToDict();
						back["MsgType"] = "GetTopic";
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "AddTeam":
						back["MsgType"] = "AddTeam";
						if (null != AddTeam(dict["TopicName"], dict["TeamName"], dict["CaptainID"], dict["Password"], ref reason)) {
							back["IfSucceed"] = "Yes";
						} else {
							back["IfSucceed"] = "No";
							back["Reason"] = reason;
						}
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "JoinTeam":
						back["MsgType"] = "JoinTeam";
						if (dataClass.TryJoinTeam(dict, ref reason)) {
							back["IfSucceed"] = "Yes";
						} else {
							back["IfSucceed"] = "No";
							back["Reason"] = reason;
						}
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "ExitTeam":
						back["MsgType"] = "ExitTeam";
						if (dataClass.TryExitTeam(dict, ref reason)) {
							back["IfSucceed"] = "Yes";
						} else {
							back["IfSucceed"] = "No";
							back["Reason"] = reason;
						}
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "LockTeam":
						back["MsgType"] = "LockTeam";
						if (dataClass.TryLockTeam(dict, ref reason)) {
							back["IfSucceed"] = "Yes";
						} else {
							back["IfSucceed"] = "No";
							back["Reason"] = reason;
						}
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "UnlockTeam":
						back["MsgType"] = "UnlockTeam";
						if (dataClass.TryUnlockTeam(dict, ref reason)) {
							back["IfSucceed"] = "Yes";
						} else {
							back["IfSucceed"] = "No";
							back["Reason"] = reason;
						}
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				case "KickMember":
						back["MsgType"] = "KickMember";
						if (dataClass.TryKickMember(dict, ref reason)) {
							back["IfSucceed"] = "Yes";
						} else {
							back["IfSucceed"] = "No";
							back["Reason"] = reason;
						}
						myNetwork.SendClient(ipAddress, jss.Serialize(back));
						break;

				default:
						MyConsole.WriteLine("Received undefined content!");
						break;
			}
		}

		public bool TryImportTask(string localFilePath) {
			StreamReader sr = File.OpenText(localFilePath);
			string content = sr.ReadToEnd();
			sr.Close();
			Dictionary<string, string> dict = jss.Deserialize<Dictionary<string, string>>(content);
			string taskName = dict["TaskName"];
			if (Array.IndexOf(GetAllTasksName(), taskName) >= 0) {
				MessageBox.Show("Task name duplicates!");
				return false;
			}
			if (!AddTask(taskName)) {
				MessageBox.Show("Task already exists");
				return false;
			}
			Task task = dataClass.GetTask(taskName);
			int nProblems = Int32.Parse(dict["NProblems"]);
			for (int i = 0; i < nProblems; ++i)
				dataClass.AddProblem(taskName, Problem.deJSON(task, dict["Problem" + i.ToString()]));
			return true;
		}

		public void ClearLimit() {
			dataClass.ClearLimit();
		}

		public HashSet<string> GetLimit() {
			return dataClass.GetLimit();
		}

		public Dictionary<string, Account> GetStudents() {
			return dataClass.GetStudents();
		}

		public void SetLimit(HashSet<string> limit) {
			dataClass.SetLimit(limit);
		}

		public void JudgeAnswers(string taskName, ref int right, ref int wrong) {
			//return dataClass.JudgeAnswers(taskName, ref right, ref wrong);
		}

		public void CreateTxtLog(string taskName, string path) {
			dataClass.CreateTxtLog(taskName, path);
		}

		public bool ContainSubmission(string taskName) {
			return dataClass.ContainSubmission(taskName);
		}

		public void CleanSubmission(string taskName) {
			dataClass.CleanSubmission(taskName);
		}

		public bool ContainTeam(string topicName) {
			return dataClass.ContainTeam(topicName);
		}

		public void CleanTeam(string topicName) {
			dataClass.CleanTeam(topicName);
		}

		public void Resplit(string sourceTopicName, List<string> targetNames) {
			List<Topic> topics = new List<Topic>(); //get all target topics
			foreach (string s in targetNames) {
				Topic topic = GetTopic(s);
				if (topic.ResidualCapacity() > 0) topics.Add(topic);
			}

			HashSet<string> ss = new HashSet<string>();  //get all student IDs
			Topic sourceTopic = GetTopic(sourceTopicName);
			foreach (Team t in sourceTopic.teams) {
				ss.Add(t.captainID);
				foreach (string studentID in t.members) ss.Add(studentID);
			}
			sourceTopic.teams.Clear();
			List<string> IDs = ss.ToList();

			int init = IDs.Count, resplit = 0;
			Random rd = new Random();
			while (topics.Count > 0 && IDs.Count > 0) {  //there exists undispatched student and spare topic
				Topic topic = topics.ElementAt(rd.Next(topics.Count));
				string captainID = IDs.ElementAt(rd.Next(IDs.Count));
				IDs.Remove(captainID);
				string reason = "";
				Team team = AddTeam(topic.topicName, captainID + "'s Team", captainID, "", ref reason);
				if (team == null) {
					MyConsole.WriteLine(reason);
					continue;
				}
				++resplit;
				for (int i = 0; i < topic.upper - 1 && IDs.Count > 0; ++i) {
					string studentID = IDs.ElementAt(rd.Next(IDs.Count));
					IDs.Remove(studentID);
					team.members.Add(studentID);
					++resplit;
				}
				if (topic.ResidualCapacity() == 0) topics.Remove(topic);
			}

			MessageBox.Show("Redistributed # of students: " + resplit.ToString() + "\nRemaining # of students: " + (init-resplit).ToString());
			
			foreach (string targetTopicName in targetNames)
				UpdateTopicPanel(GetTopic(targetTopicName));  // update target topic's panel
			if (IDs.Count == 0) {  // no remaining student
				UpdateTopicPanel(sourceTopic);
				return;
			}
			string r = "";
			Team remainTeam = AddTeam(sourceTopicName, "Remaining Group", IDs.First(), "", ref r); //Put remaining students in a new team
			MyConsole.WriteLine(r);
			IDs.RemoveAt(0);  //eliminate the captain
			foreach (string sID in IDs) remainTeam.members.Add(sID);
			UpdateTopicPanel(sourceTopic);
		}

		public AllAnswer GetAllAnswer(string taskName) {
			return dataClass.GetAllAnswer(taskName);
		}

		public void ExportTopicsAndTeams(string path) {
			dataClass.ExportTopicsAndTeams(path);
		}

		public void UpdateAnalysisPanel(AllAnswer allAnswer) {
			mainForm.UpdateTabPage(allAnswer);
		}
	}
}
